// 此文件用于统一整合其他的类，并统一暴露出去
import Snake from './Snake'
import Food from './Food'
import ScorePanel from './ScorePanel'

export default class Control {
  timer:number = 0     // 定时器id
  Snake: Snake
  Food: Food
  ScorePanel: ScorePanel
  direction: string = '' // 存储用户按下的方向键
  // 目前是否存活，默认 ture
  isLive: boolean = true
  mask:HTMLElement    // 遮罩层
  constructor() {
    this.ScorePanel = new ScorePanel()
    this.Snake = new Snake()
    this.Food = new Food()
    this.mask = document.querySelector('.mask') as HTMLElement
  }
  // 开始游戏的方法
  start() {
    this.ScorePanel.setTip()    // 显示 tip
    this.isLive = true
    this.direction = ''
    // 先尝试读取历史记录
    this.ScorePanel.checkHistory()
    // 隐藏遮罩层
    this.mask.style.display = 'none'
    // 监听键盘按下的事件,注意：这里的 keyDownHandler 函数的调用者是 document 所以要改变 this 指向问题
    document.addEventListener('keydown', this.keyDownHandler.bind(this))
    // 给蛇头位置重置一下
    this.Snake.resetSnake()
    // 给食物的位置重置一下
    this.Food.change()
    // 在此调用 run 方法
    this.run()
  }
  // 键盘按下事件
  keyDownHandler(e: KeyboardEvent) {
    const key = e.key
    // 只有是方向键才处理
    if (
      key === 'ArrowUp' ||
      key === 'Up' ||
      key === 'ArrowDown' ||
      key === 'Down' ||
      key === 'ArrowLeft' ||
      key === 'Left' ||
      key === 'ArrowRight' ||
      key === 'Right'
    ) {
      this.direction = e.key
      this.ScorePanel.setTip(false)    // 隐藏 tip
    }
  }
  // 让蛇移动(根据方向键)
  run() {
    // 获取蛇的坐标
    let X = this.Snake.X
    let Y = this.Snake.Y
    // 判断按下的是否为 上下左右 四个方向键中的一个
    // ArrowUp/ArrowDown/ArrowLeft/ArrowRight
    switch (this.direction) {
      case 'ArrowUp':
      case 'Up': // 这里是IE里的方向键的 key
        Y -= 10
        break
      case 'ArrowDown':
      case 'Down':
        Y += 10
        break
      case 'ArrowLeft':
      case 'Left':
        X -= 10
        break
      case 'ArrowRight':
      case 'Right':
        X += 10
        break
    }
    // 检查蛇是否吃到了食物
    this.checkEat(X, Y)

    // 最后将蛇新的位置坐标赋值给蛇
    // 在此捕获异常，如果蛇撞墙了就会抛出异常
    try {
      this.Snake.X = X
      this.Snake.Y = Y
    } catch (error) {       // 游戏结束
      clearTimeout(this.timer)
      // 显示遮罩层
      this.mask.style.display = 'flex'
      // 判断历史数据是否需要更新
      this.ScorePanel.checkHistory()
      this.Snake.resetSnake()   // 清空蛇的身体数据
      this.ScorePanel.resetData()   // 重置记分牌数据
      this.Food.change()      // 重新生成一个食物
      this.isLive = false // 游戏结束
    }

    // 对蛇移动的间隔时间做一些处理可以调整游戏难易程度：间隔时间越长移动越慢游戏越简单
    const time: number = 300 - (this.ScorePanel.level - 1) * 30
    // 在此校验游戏是否结束，没有结束则开启定时器循环调用 run 方法让蛇持续的“动”
    this.isLive && (this.timer = setTimeout(this.run.bind(this), time))
  }
  // 定义一个检查蛇是否吃到食物的方法
  checkEat(X: number, Y: number) {
    if (X === this.Food.X && Y === this.Food.Y) {
      this.ScorePanel.addScore() // 加一分
      this.Food.change() // 重新刷新食物的位置
      this.Snake.addBody() // 蛇的身体加一节
    }
  }
}
